#define STB_IMAGE_IMPLEMENTATION
#include "Texture.h"
#include "GL/glew.h"
#include "graphics/GLState.h"
#include "graphics/GLTextures.h"
#include <iostream>
#include <stb_image.h>
using namespace Umikaze::Core;

Texture::~Texture() { GlTextures::instance()->deleteTexture(_textureId); }

ImageTexture::ImageTexture(const std::string &path, bool flip, bool hdr) {
  stbi_set_flip_vertically_on_load(flip);
  if (hdr) {
    _data = stbi_loadf(path.c_str(), &_width, &_height, &_channel, 0);
  } else {
    _data = stbi_load(path.c_str(), &_width, &_height, &_channel, 0);
  }

  stbi_set_flip_vertically_on_load(false);
  if (!_data) {
    std::cerr << "Failed to load texture" << std::endl;
  } else {
    // TODO: 这个应该放到第一次draw图片时，以后要优化
    auto &gltextures = GlTextures::instance();
    _textureId = gltextures->genTexture();
    if (hdr) {
      if (_channel == 3) {
        gltextures->updateTexture2D(_textureId, _data, _width, _height,
                                    GL_RGB16F, GL_RGB, GL_FLOAT);
      } else {
        gltextures->updateTexture2D(_textureId, _data, _width, _height,
                                    GL_RGBA16F, GL_RGBA, GL_FLOAT);
      }
    } else {
      if (_channel == 3) {
        gltextures->updateTexture2D(_textureId, _data, _width, _height, GL_RGB,
                                    GL_RGB, GL_UNSIGNED_BYTE);
      } else {
        gltextures->updateTexture2D(_textureId, _data, _width, _height, GL_RGB,
                                    GL_RGBA, GL_UNSIGNED_BYTE);
      }
    }
  }
}

ImageTexture::~ImageTexture() { stbi_image_free(_data); }

void ImageTexture::bind(uint32_t location) {
  auto &gltextures = GlTextures::instance();
  gltextures->setTexture2D(_textureId, location);
}

RenderTexture::RenderTexture(int width, int height, int depth,
                             ColorFormat colorFormat) {
  _depth = depth;
  _width = width;
  _height = height;
  _colorFormat = colorFormat;
  auto &glTextures = GlTextures::instance();
  // TODO: 同上，另外这个格式问题，也需要处理
  _textureId = glTextures->genTexture();

  GLenum format = 0;
  if (depth > 0) {
    if (colorFormat == ColorFormat::RG16) {
      glTextures->updateTexture2DArray(_textureId, nullptr, _width, _height,
                                       _depth, GL_RG16, GL_RG16);
    } else if (colorFormat == ColorFormat::RGBA8888) {
      glTextures->updateTexture2DArray(_textureId, nullptr, _width, _height,
                                       _depth, GL_RGBA8, GL_RGBA8);
    }
  } else {
    if (colorFormat == ColorFormat::RG16) {
      glTextures->updateTexture2D(_textureId, nullptr, _width, _height, GL_RG16,
                                  GL_RGBA, GL_UNSIGNED_BYTE);
    } else if (colorFormat == ColorFormat::RGBA8888) {
      glTextures->updateTexture2D(_textureId, nullptr, _width, _height,
                                  GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
    }
  }
}

RenderTexture::RenderTexture(int width, int height, ColorFormat colorFormat)
    : RenderTexture(width, height, 0, colorFormat) {}

RenderTexture::RenderTexture(int width, int height, int depth,
                             DepthStencilFormat depthStencilFormat)
    : _depthStencilFormat(depthStencilFormat) {
  _depth = depth;
  _width = width;
  _height = height;
  auto &glTextures = GlTextures::instance();
  _textureId = glTextures->genTexture();
  auto format = getFormat();
  if (_depth > 0) {
    glTextures->updateTexture2DArray(_textureId, nullptr, _width, _height,
                                     _depth, format, GL_DEPTH_COMPONENT);
  } else {
    glTextures->updateTexture2D(_textureId, nullptr, _width, _height, format,
                                GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE);
  }
}

RenderTexture::RenderTexture(int width, int height,
                             DepthStencilFormat depthStencilFormat)
    : RenderTexture(width, height, 0, depthStencilFormat) {}

void RenderTexture::bindAsColorAttachment(uint32_t location) {
  auto &glTextures = GlTextures::instance();
  glTextures->setTextureAttachment(_textureId, location);
}

void RenderTexture::bindAsDepthAttachment() {
  auto &textures = GlTextures::instance();
  switch (_depthStencilFormat) {
  case DepthStencilFormat::DEPTH24_STENCIL8: {
    textures->setDepthStencilAttachment(_textureId);
  } break;
  case DepthStencilFormat::DEPTH16: {
    textures->setDepthAttachment(_textureId);

  } break;
  case DepthStencilFormat::DEPTH24: {
    textures->setDepthAttachment(_textureId);
  } break;
  default:
    std::cerr << "请输入正确的depthFormat" << std::endl;
    return;
    break;
  }
}

GLenum RenderTexture::getFormat() {
  switch (_depthStencilFormat) {
  case DepthStencilFormat::DEPTH24_STENCIL8: {
    return GL_DEPTH24_STENCIL8;
  } break;
  case DepthStencilFormat::DEPTH16: {
    return GL_DEPTH_COMPONENT16;
  } break;
  case DepthStencilFormat::DEPTH24: {
    return GL_DEPTH_COMPONENT24;
  } break;
  default:
    std::cerr << "请输入正确的depthFormat" << std::endl;
    return GL_FALSE;
    break;
  }
}

void RenderTexture::bind(uint32_t location) {
  auto &gltextures = GlTextures::instance();
  if (_depth > 0) {
    gltextures->setTexture2DArray(_textureId, location);
  } else {
    gltextures->setTexture2D(_textureId, location);
  }
}

CubeTexture::CubeTexture(const std::string &dirPath,
                         const std::string &suffix) {
  // stbi_set_flip_vertically_on_load(flip);

  std::string name[] = {"right", "left", "top", "bottom", "front", "back"};

  for (uint32_t i = 0; i < 6; ++i) {
    std::string path = (dirPath + name[i] + suffix);
    _faces[i] = stbi_load(path.c_str(), &_width, &_height, &_channel, 0);
    if (!_faces[i]) {
      std::cerr << "Failed to load texture" << std::endl;
    }
  }

  auto &gltextures = GlTextures::instance();
  _textureId = gltextures->genTexture();
  if (_channel == 3) {
    gltextures->updateTextureCubeMap(_textureId, _faces, _width, _height,
                                     GL_RGB);
  } else {
    gltextures->updateTextureCubeMap(_textureId, _faces, _width, _height,
                                     GL_RGBA);
  }
}

void CubeTexture::bind(uint32_t location) {
  auto &gltextures = GlTextures::instance();
  gltextures->setTextureCubeMap(_textureId, location);
}

CubeTexture::~CubeTexture() {

  for (uint32_t i = 0; i < 6; ++i) {
    stbi_image_free(_faces[i]);
  }
}